import operator


class Expr(object):
    __slots__ = ["value"]
    def __init__(self, value = None):
        self.value = value
    def eval(self, *args):
        return self.value
    def __call__(self, *args, **kwargs):
        return CallExpr(self, args, kwargs)
    def __repr__(self):
        return repr(self.value)
    def __getattr__(self, name):
        return GetattrExpr(self, name)
    def __getitem__(self, key):
        return GetitemExpr(self, key)
    def __add__(self, other):
        return AddExpr(self, other)
    def __sub__(self, other):
        return SubExpr(self, other)
    def __mul__(self, other):
        return MulExpr(self, other)
    def __div__(self, other):
        return DivExpr(self, other)
    def __mod__(self, other):
        return ModExpr(self, other)
    def __pow__(self, other):
        return PowExpr(self, other)
    def __and__(self, other):
        return AndExpr(self, other)
    def __or__(self, other):
        return OrExpr(self, other)
    def __invert__(self, other):
        return InvExpr(self, other)
    def __eq__(self, other):
        return EqExpr(self, other)
    def __ne__(self, other):
        return NeExpr(self, other)
    def __lt__(self, other):
        return LtExpr(self, other)
    def __le__(self, other):
        return LeExpr(self, other)
    def __gt__(self, other):
        return GtExpr(self, other)
    def __ge__(self, other):
        return GeExpr(self, other)

class Arg(Expr):
    __slots__ = ["index", "name"]
    def __init__(self, index, name = None):
        assert name is None or type(name) is str 
        self.index = index
        self.name = name
    def __repr__(self):
        if self.name is None:
            return "Arg(%r)" % (self.index,)
        else:
            return self.name
    def eval(self, *args):
        return args[self.index]

class GetattrExpr(Expr):
    __slots__ = ["expr", "name"]
    def __init__(self, expr, name):
        self.expr = expr
        self.name = name
    def __repr__(self):
        return "%r.%s" % (self.expr, self.name)
    def eval(self, *args):
        return getattr(self.expr.eval(*args), self.name)

class GetitemExpr(Expr):
    __slots__ = ["expr", "key"]
    def __init__(self, expr, key):
        self.expr = expr
        self.key = key
    def __repr__(self):
        return "%r[%r]" % (self.expr, self.key)
    def eval(self, *args):
        return self.expr.eval(*args)[self.name]

class CallExpr(Expr):
    __slots__ = ["expr", "args", "kwargs"]
    def __init__(self, expr, args, kwargs):
        self.expr = expr
        self.args = args
        self.kwargs = kwargs
    def __repr__(self):
        args = [repr(a) for a in self.args]
        args.extend("%s = %r" % (k, v) for k, v in self.kwargs.iteritems())
        return "%r(%s)" % (self.expr, ", ".join(args))
    def eval(self, *args):
        return self.expr.eval(*args)(*self.args, **self.kwargs)

class BinaryExpr(Expr):
    __slots__ = ["rhs", "lhs"]
    op = "???"
    def __init__(self, lhs, rhs):
        self.lhs = lhs
        self.rhs = rhs
    def __repr__(self):
        return "(%r %s %r)" % (self.lhs, self.op, self.rhs)
    def eval(self, *args):
        lhs = self.lhs.eval(*args) if hasattr(self.lhs, "eval") else self.lhs
        rhs = self.rhs.eval(*args) if hasattr(self.rhs, "eval") else self.rhs
        return self._eval(lhs, rhs)


class AddExpr(BinaryExpr): __slots__ = []; op = "+"; _eval = operator.add
class SubExpr(BinaryExpr): __slots__ = []; op = "-"; _eval = operator.sub
class MulExpr(BinaryExpr): __slots__ = []; op = "*"; _eval = operator.mul
class DivExpr(BinaryExpr): __slots__ = []; op = "/"; _eval = operator.div
class ModExpr(BinaryExpr): __slots__ = []; op = "%"; _eval = operator.mod
class PowExpr(BinaryExpr): __slots__ = []; op = "**"; _eval = operator.pow
class AndExpr(BinaryExpr): __slots__ = []; op = "and"; _eval = operator.and_
class OrExpr(BinaryExpr): __slots__ = []; op = "or"; _eval = operator.or_
class InvExpr(BinaryExpr): __slots__ = []; op = "!"; _eval = operator.invert
class InExpr(BinaryExpr): __slots__ = []; op = "in"; _eval = operator.contains
class EqExpr(BinaryExpr): __slots__ = []; op = "=="; _eval = operator.eq
class NeExpr(BinaryExpr): __slots__ = []; op = "!="; _eval = operator.ne
class LtExpr(BinaryExpr): __slots__ = []; op = "<"; _eval = operator.lt
class LeExpr(BinaryExpr): __slots__ = []; op = "<="; _eval = operator.le
class GtExpr(BinaryExpr): __slots__ = []; op = ">"; _eval = operator.gt
class GeExpr(BinaryExpr): __slots__ = []; op = ">="; _eval = operator.ge




if __name__ == "__main__":
    e = (Arg(0) + 17 >= 8) & ("bacon" != Arg(1))
    print e
    print e.eval(9, "bacoN")















